package com.chuangke.aspect;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSONObject;
import com.chuangke.admin.entity.SysUser;
import com.chuangke.admin.service.SysUserService;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.common.http.HttpResult;
import com.chuangke.common.utils.HttpServletRequestUtils;
import com.chuangke.common.utils.HttpUtils;
import com.chuangke.common.utils.SecurityUtils;
import com.chuangke.common.utils.SpringContextUtil;
import com.chuangke.framework.service.CommonQueryService;
import com.chuangke.system.annotation.OptLogDoc;
import com.chuangke.system.entity.SysOptLog;
import com.chuangke.system.enums.LogBisType;
import com.chuangke.system.service.SysOptLogService;

import cn.hutool.core.util.StrUtil;

/**
 * 系统日志，切面处理类，记录日志
 */
@Aspect
@Component
public class SysOptLogAspect {

	private static final Logger logger = LoggerFactory.getLogger(SysOptLogAspect.class);

	@Autowired
	private SysOptLogService sysOptLogService;
	
	@Autowired
	private SysUserService sysUserService ;
	
	private final ExpressionParser parser = new SpelExpressionParser();
	private final ParameterNameDiscoverer parameterNameDiscoverer =  new DefaultParameterNameDiscoverer() ;

	@Around("@annotation(com.chuangke.system.annotation.OptLogDoc)")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		long beginTime = System.currentTimeMillis();
		// 执行方法
		Object result = point.proceed();
		// 执行时长(毫秒)
		long time = System.currentTimeMillis() - beginTime;
		// 保存日志
		saveSysOptLog(point, time, result);
		return result;
	}
	
	@SuppressWarnings("rawtypes")
	private void saveSysOptLog(ProceedingJoinPoint joinPoint, long time,Object result) {
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
	        Method method = signature.getMethod();
	        OptLogDoc optLogDoc = method.getAnnotation(OptLogDoc.class);
	        
	        Object idValueObject = getValue(optLogDoc.objId(),result, method, joinPoint.getArgs()) ;
	        Object nameValueObject = getValue(optLogDoc.objName(),result, method, joinPoint.getArgs()) ;
	        Object carrierIdValueObject = getValue(optLogDoc.objCarrierId(),result, method, joinPoint.getArgs()) ;

	        String userName = SecurityUtils.getUserId();
			String status = "0" ;
			if(result instanceof HttpResult) {
				if(((HttpResult) result).getCode()==200) {
					status = "1" ;
				}
			}
	        int listSize = getLogSize(idValueObject,carrierIdValueObject) ;
	        List<SysOptLog> list = new ArrayList<>(listSize) ;
	        for(int i=0; i<listSize; i++) {
	        	SysOptLog log = new SysOptLog() ;
	        	
	        	//给objId objName设值
	        	if(idValueObject instanceof String) {
	        		log.setObjId(idValueObject.toString());
	        		log.setObjName(nameValueObject==null?null:nameValueObject.toString());
	        	}else if(idValueObject instanceof List) {
	        		log.setObjId(((List)idValueObject).get(i).toString());
	        		String objName = null ;
	        		if(nameValueObject!= null) {
	        			objName=((List)nameValueObject).get(i)==null ? null:((List)nameValueObject).get(i).toString() ;
	        		}
	        		log.setObjName(objName);
	        	}
	        	
	        	//给objCarrierId设值
	        	if(carrierIdValueObject instanceof String) {
	        		log.setObjCarrierId(carrierIdValueObject.toString());
	        	}else if(carrierIdValueObject instanceof List) {
	        		log.setObjCarrierId(((List)carrierIdValueObject).get(i).toString());
	        	}
	        	
	        	log.setOptType(optLogDoc.optType().name());
	        	log.setObjType(optLogDoc.objType().name());
	        	log.setMemo(optLogDoc.name());
	        	
	        	log.setUserId(userName);
	        	log.setCreateTime(new Date());
	        	log.setReplyCount(0);
	        	log.setTime(time);
	        	log.setAppId("1");
				
	        	log.setIpaddr(HttpServletRequestUtils.getIpAddr(HttpUtils.getHttpServletRequest())) ;
	        	log.setParam(getParams(joinPoint));
	        	log.setResult(JSONObject.toJSONString(result));
	        	log.setStatus(status);
	        	log.setRequestType(HttpUtils.getHttpServletRequest().getMethod());
	        	log.setOs(HttpServletRequestUtils.getOs(HttpUtils.getHttpServletRequest()));
	        	log.setBrowser(HttpServletRequestUtils.getBrowser(HttpUtils.getHttpServletRequest()));
				
				SysUser user = sysUserService.findById(userName) ;
				log.setDeptId(user== null ? "":user.getDeptId());
				
				if(StrUtil.isBlank(optLogDoc.objCarrierId())) {
					log.setObjCarrierId(log.getDeptId());
				}
				
				list.add(log) ;
	        }
	        
	        setObjName(list.stream().filter(opt->StrUtil.isBlank(opt.getObjName())).collect(Collectors.toList()),optLogDoc.objType()) ;
	        
	        sysOptLogService.asyncSave(list);
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void setObjName(List<SysOptLog> optList,LogBisType bisType) {
		if(CollectionUtils.isEmpty(optList)) {
			return ;
		}
		if(StrUtil.isBlank(bisType.getService())) {
			return ;
		}
		
		try {
			CommonQueryService taskQueryService = (CommonQueryService) SpringContextUtil
					.getBean(bisType.getService());
			List<String> ids = optList.stream().map(SysOptLog::getObjId).distinct()
					.collect(Collectors.toList());
			List beanList = taskQueryService.list(ids);
			if (!CollectionUtils.isEmpty(beanList)) {
				Map<String, Object> beanMap = (Map<String, Object>) beanList.stream()
						.collect(Collectors.toMap(bean -> {
							try {
								return BeanUtils.getProperty(bean, bisType.getIdCol()) ;
							} catch (Exception e) {
								e.printStackTrace();
								throw new ChuangkeException(e) ;
							}
						}, a -> a));
				optList.forEach(obj -> {
					try {
						obj.setObjName(BeanUtils.getProperty(beanMap.get(obj.getObjId()), bisType.getNameCol()));
					} catch (Exception e) {
						e.printStackTrace();
					}
				});
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private Object getValue(String expression, Object rootObject, Method method, Object[] arguments) {
		Object idValueObject = null ;
        if(!StrUtil.isBlank(expression)) {
        	EvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, arguments,parameterNameDiscoverer);
            idValueObject = parser.parseExpression(expression).getValue(context);
        }
        return idValueObject ;
	}
	
	@SuppressWarnings("rawtypes")
	private int getLogSize(Object idValueObject,Object carrierIdValueObject) {
		int size = 1 ;
        if(idValueObject instanceof List) {
        	size = ((List)idValueObject).size() ;
        }else if((!(idValueObject instanceof List)) && (carrierIdValueObject instanceof List)) {
        	size = ((List)carrierIdValueObject).size() ;
        }
        return size ;
	}
	
	private String getParams(ProceedingJoinPoint joinPoint) {
		StringBuilder params = new StringBuilder();
		Object[] args = joinPoint.getArgs();
		for (int i = 0; i < args.length; i++) {
			if(args[i] instanceof HttpServletRequest) {
				continue ;
			}
			
			params.append("Param").append(i).append(":");
			try {
				if(args[i] instanceof MultipartFile) {
					params.append("文件上传");
				}else {
//					SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
//					filter.getExcludes().add("password");
					params.append(JSONObject.toJSONString(args[i]));
				}
			} catch (Exception e) {
				params.append("Parse failured!");
			}
			params.append(" ; ");
		}
		return params.length() > 5000 ? params.substring(0, 4900) + "..." : params.toString();
	}

}
